﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FlatRedBall;
using FlatRedBall.Math.Geometry;
using FlatRedBall.Graphics.Animation;
using FlatRedBall.Content.AnimationChain;

namespace Runaway
{
    public class Player : PositionedObject
    {

        /*
         * These states will determine the player's behavior and should only be changed
         * in the GameManger class. The behavior for each sate is defined in the Player.Update()
         * method.         * 
         */
        public enum state
        {
            Standing,
            Running,
            Sliding,
            Jumping,
            WallSliding,
            Attacking

        }

        public enum animationState
        {
            Idle,
            Running,
            Sliding,
            Jumping,
            Falling,
            Wallsliding,
            Walljumping,
            Attacking
        }

        private Sprite sprite;
        private AnimationChainList claireAnimation;
        private const float XACCELERATION = 1f;
        private const float GRAVITY = .5f;
        private const float MAX_SPEED = 30;
        private const float MAX_FALL_SPEED = -30;
        private const float MAX_SLIDING_SPEED = -6;
        private const float SLIDING_DECELERATION = .05f;
        private const float WALL_SLIDING_DECELERATION = .5f;
        private const float DECELERATION = .2f;
        private const float JUMP = 20;
        private const float MIN_X_SPEED = 1f;
        private AxisAlignedRectangle hitBox;
        private state playerState;
        private int money;
        private animationState animateState;


        //constructor
        public Player(float x, float y)
        {
            SpriteManager.AddPositionedObject(this);
            SpriteManager.Camera.AttachTo(this, true);

            this.X = x;
            this.Y = y;

            //add sprite
            claireAnimation = FlatRedBallServices.Load<AnimationChainList>("Content/Claire/Idle/Claire_Idle", "Global");
            sprite = SpriteManager.AddSprite(claireAnimation);
            sprite.AttachTo(this, false);
            sprite.PixelScale();
            playerState = state.Standing;
            money = 0; // =(
            animateState = animationState.Idle;

            //add hitbox
            hitBox = ShapeManager.AddAxisAlignedRectangle();
            hitBox.AttachTo(this, false);
            hitBox.ScaleY = sprite.ScaleY;
            hitBox.ScaleX = sprite.ScaleX * .45f;
        }

        // Helper function to make code easier to read
        private void MoveHorizontal()
        {
            if (Input.getMoveLeftInput() == true)
            {
                //decelerate if we are already running in the opposite direction
                if (this.Velocity.X > 0)
                {
                    this.Velocity.X -= this.Velocity.X * DECELERATION;
                }
                this.Velocity.X -= XACCELERATION;
            }
            else if (Input.getMoveRightInput() == true)
            {
                //decelerate if we are already running in the opposite direction
                if (this.Velocity.X < 0)
                {
                    this.Velocity.X -= this.Velocity.X * DECELERATION;
                }
                this.Velocity.X += XACCELERATION;
            }
        }

        //Update function
        public virtual void Update()
        {
            
            // Attack + attack animation
            if (playerState == state.Attacking || animateState == animationState.Attacking)
            {
                // Stop attacking if interrupted or finished
                if (playerState != state.Attacking || sprite.JustCycled)
                {
                    changeAnimation(animationState.Idle);
                    changeState(state.Jumping);
                }
                else
                {
                    playerState = state.Attacking;
                    // >> ATTACK CODE HANDLING GOES HERE <<
                }
            }
            if (Input.getAttackInput() == true && (playerState == state.Standing || playerState == state.Running))
            {
                changeState(state.Attacking);
                changeAnimation(animationState.Attacking);
                sprite.AnimationSpeed = 15f;
            }

            //Running
            else if (playerState == state.Running)
            {
                if (animateState != animationState.Running)
                {
                    changeAnimation(animationState.Running);
                }
                MoveHorizontal();
                //move back into idle state
                if (this.Velocity.X == 0 && animateState != animationState.Idle)
                {
                    changeAnimation(animationState.Idle);
                }

            }

            //Jumping
            else if (playerState == state.Jumping)
            {
                //if (animateState != animationState.Jumping)
                //{
                //    changeAnimation(animationState.Jumping);
                //}
                MoveHorizontal();
                //move back into idle state
                if (this.Velocity.X == 0 && this.Velocity.Y == 0 && animateState != animationState.Idle)
                {
                    changeAnimation(animationState.Idle);
                }
            }

            //Wall Sliding
            else if (playerState == state.WallSliding)
            {
                //if (animateState != animationState.Wallsiding)
                //{
                //    changeAnimation(animationState.Wallsliding);
                //}
                this.Velocity.Y -= GRAVITY * WALL_SLIDING_DECELERATION;
                if (this.Velocity.Y < MAX_SLIDING_SPEED)
                {
                    this.Velocity.Y = MAX_SLIDING_SPEED;
                }
                MoveHorizontal();
            }
            
            //Sliding
            else if (playerState == state.Sliding)
            {
                //if (animateState != animationState.Sliding)
                //{
                //    changeAnimation(animationState.Sliding);
                //}
                this.Velocity.X -= this.Velocity.X * SLIDING_DECELERATION;
            }

            // Leave in these two else if / else blocks. They seem redundant,
            //  but they help avoid a bug situation of animation state limbo.
            else if (playerState == state.Standing)
            {
                if (animateState != animationState.Idle)
                {
                    changeAnimation(animationState.Idle);
                }
                this.Velocity.X -= this.Velocity.X * DECELERATION;
            }
            else
            {
                this.Velocity.X -= this.Velocity.X * DECELERATION;
            }

            // Jumping input
            if (Input.getJumpInput() && playerState != state.Jumping)
            {
                if (playerState == state.WallSliding)
                {
                    //if there is any object to the left of the player
                    if (hitBox.LastMoveCollisionReposition.X > 0)
                    {
                        this.Velocity.X += (float)Math.Cos(45) * 2 * JUMP;
                    }
                    else // must be to the right
                    {
                        this.Velocity.X -= (float)Math.Cos(45) * 2 * JUMP;
                    }
                }
                this.Velocity.Y = JUMP;
                playerState = state.Jumping;
            }

            // Motion
            this.Velocity.Y -= GRAVITY;
            if (this.Velocity.Y < MAX_FALL_SPEED)
            {
                this.Velocity.Y = MAX_FALL_SPEED;
            }

            if (Math.Abs(this.Velocity.X) > MAX_SPEED)
            {
                this.Velocity.X = Math.Sign(this.Velocity.X) * MAX_SPEED;
            }
            else if (Math.Abs(this.Velocity.X) < MIN_X_SPEED)
            {
                this.Velocity.X = 0;
            }
            
            

        }

        //code to change the animations
        private void changeAnimation(animationState newState)
        {
            SpriteManager.RemoveSprite(sprite);
            ShapeManager.Remove(hitBox);
            animateState = newState;
            switch (newState)
            {
                case animationState.Idle:
                    claireAnimation = FlatRedBallServices.Load<AnimationChainList>("Content/Claire/Idle/Claire_Idle", "Global");
                    break;
                case animationState.Running:
                    claireAnimation = FlatRedBallServices.Load<AnimationChainList>("Content/Claire/Running/Claire_Running", "Global");
                    break;
                case animationState.Attacking:
                    claireAnimation = FlatRedBallServices.Load<AnimationChainList>("Content/Claire/Attacking/Claire_Attacking", "Global");
                    break;
                case animationState.Falling:
                    // ...
                    break;
                case animationState.Jumping:
                    // ...
                    break;
                case animationState.Sliding:
                    // ...
                    break;
                case animationState.Wallsliding:
                    claireAnimation = FlatRedBallServices.Load<AnimationChainList>("Content/Claire/WallSliding/Claire_Wall_Sliding", "Global");
                    break;
            }

            sprite = SpriteManager.AddSprite(claireAnimation);
            sprite.AttachTo(this, false);
            sprite.PixelScale();

            //set hitbox
            hitBox = ShapeManager.AddAxisAlignedRectangle();
            hitBox.AttachTo(this, false);
            hitBox.ScaleY = sprite.ScaleY;
            hitBox.ScaleX = sprite.ScaleX * .45f;
        }

        public void changeState(state newState)
        {
            playerState = newState;
        }

        public state getState()
        {
            return (playerState);
        }
        public void getMoney(int value)
        {
            money += value;
        }

        public AxisAlignedRectangle getHitBox()
        {
            return (hitBox);
        }

        public void Destroy()
        {
            SpriteManager.RemoveSprite(sprite);
        }
    }
}
